package com.zoro.dyformservice.support.web.converter;

import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.zoro.dyformservice.interfaces.CodeData;
import com.zoro.dyformservice.utils.CollectionUtils;
import com.zoro.dyformservice.utils.JsonUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;

import java.lang.reflect.InvocationTargetException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.Set;

public class CustomJsonSerializerConfig {


    private final Logger logger = LoggerFactory.getLogger(getClass());


    @SuppressWarnings({"SpringJavaInjectionPointsAutowiringInspection", "rawtypes"})
    @Bean
    public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer(CustomJsonSerializerProvider provider) {
        return jacksonObjectMapperBuilder -> {
            // LocalDateTime
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            SimpleModule javaTimeModule = new JavaTimeModule()
                    .addSerializer(
                            LocalDateTime.class,
                            new LocalDateTimeSerializer(formatter))
                    .addDeserializer(
                            LocalDateTime.class,
                            new LocalDateTimeDeserializer(formatter)
                    );
            SimpleModule module = new SimpleModule();
            module.addSerializer(Date.class, JsonUtil.makeDateSerializer());
            module.addDeserializer(Date.class, JsonUtil.makeDateDeserializer());
            jacksonObjectMapperBuilder
                    // 这个设置会让ObjectMapper 变得线程不安全
                    .simpleDateFormat("yyyy-MM-dd HH:mm:ss")
                    .modules(javaTimeModule, module)
            ;

            Set<Class<?>> classSet = provider.javaClass();
            Set<Class<CodeData>> codeDataClass = provider.codeDataType();

            if (CollectionUtils.isEmpty(classSet) && CollectionUtils.isEmpty(codeDataClass)) {
                logger.warn("There is no JsonSerializer and CodeData type.");
                return;
            }

            if (classSet != null) {
                for (Class<?> clazz : classSet) {
                    CustomJsonSerializerType serializerAnno = clazz.getAnnotation(CustomJsonSerializerType.class);
                    if (serializerAnno == null) {
                        continue;
                    }

                    Class<? extends CustomJsonSerializer<?>> serializerProvider = serializerAnno.use();
                    logger.debug("register custom json serializer {} for {}", serializerProvider, clazz);
                    CustomJsonSerializer<?> serializer;
                    try {
                        serializer = serializerProvider.getConstructor().newInstance();

                    } catch (NoSuchMethodException | InvocationTargetException | InstantiationException |
                             IllegalAccessException e) {
                        throw new RuntimeException(e);
                    }
                    jacksonObjectMapperBuilder.serializerByType(clazz, serializer.serializer());
                    jacksonObjectMapperBuilder.deserializerByType(clazz, serializer.deserializer());
                }
            }
            if (codeDataClass != null) {
                for (Class<? extends CodeData> c : codeDataClass) {
                    logger.debug("register custom json serializer {} for {}", "CodeDataSerializer", c);
                    jacksonObjectMapperBuilder.serializerByType(c, new CodeDataSerializer<>());
                    jacksonObjectMapperBuilder.deserializerByType(c, new CodeDataDeserializer<>(c));
                }
            }
        };
    }
}
